'''
Created on 24/04/2009

@author: eh01
'''
from yarra.datastore import sqlite3dao, dsmanager
from yarra.datastore.dao import UpdateDAOException, NotFoundDAOException, \
    ConcurrencyDAOException
from yarra.datastore.dso import DSO
from yarra.datastore.dst import DST
from yarra.datastore.sqlite3dao import DSOSqlite3DAO
import datetime
import os
import sqlite3
import unittest

DEFAULT_VERSION = 1

class SQLite3DAOTest(unittest.TestCase):

    def get_dummy_unsaved_dso_1(self):
        dst1 = DST(subject="dummySubject1", predicate="dummyPredicate1", object=None, literal="123", literal_type="int", xml_string=None)
        dst2 = DST(subject="dummySubject1", predicate="dummyPredicate2", object=None, literal="456", literal_type="int", xml_string=None)
        dso = DSO(created_timestamp=datetime.datetime.now())
        dso.type = "DummyDSO1"
        dso.uri = "/dummy/object1/uri"
        dso.add_dsts([dst1, dst2])
        return dso
    
    def get_dummy_unsaved_dso_2(self):
        dst1 = DST(subject="dummySubject1", predicate="dummyPredicate1", object=None, literal="123", literal_type="int", xml_string=None)
        dst2 = DST(subject="dummySubject1", predicate="dummyPredicate2", object=None, literal="456", literal_type="int", xml_string=None)
        dst3 = DST(subject="dummySubject2", predicate="dummyPredicate3", object=None, literal="789", literal_type="int", xml_string=None)
        dso = DSO(created_timestamp=datetime.datetime.now())
        dso.type = "DummyDSO1"
        dso.uri = "/dummy/object2/uri"
        dso.add_dsts([dst1, dst2, dst3])
        return dso    
    
    def get_dummy_saved_dso_1(self):
        dst1 = DST(subject="dummySubject1", predicate="dummyPredicate1", object=None, literal="123", literal_type="int", xml_string=None)
        dst2 = DST(subject="dummySubject1", predicate="dummyPredicate2", object=None, literal="456", literal_type="int", xml_string=None)
        dst3 = DST(subject="dummySubject2", predicate="dummyPredicate3", object=None, literal="789", literal_type="int", xml_string=None)
        dso = DSO(id=123, version=DEFAULT_VERSION, created_timestamp=datetime.datetime.now())
        dso.type = "DummyDSO1"
        dso.uri = "/dummy/object/uri"
        dso.add_dsts([dst1, dst2, dst3])
        return dso        

    def get_dummy_unsaved_dst_1(self):
        return DST(subject="dummySubject1", predicate="dummyPredicate1", object=None, literal="123", literal_type="int", xml_string=None)
    
    def get_dummy_saved_dst_1(self):
        return DST(id=123, subject="dummySubject1", predicate="dummyPredicate1", object=None, literal="123", literal_type="int", xml_string=None)
    
    def setUp(self):
#        sqlite3dao.get_connection = memory_get_connection
        dsmanager.initialize()

    def tearDown(self):
        # delete the database file when done
        os.remove(sqlite3dao.DATABASE_NAME)
#        pass

    def testPersistCase1(self):
        """ Tests the dso is saved successfully """
        dSOSqlite3DAO = DSOSqlite3DAO()
        dso = self.get_dummy_unsaved_dso_1()
        dso = dSOSqlite3DAO.save([dso])[0]
        if dso and dso.id:
            assert True
        else:
            self.fail("DSO not return with generated id")

    def testPersistCase2(self):
        """ Tests that the dso cannot be updated as the dso id does not exists in the database """
        dSOSqlite3DAO = DSOSqlite3DAO()
        # this dso has an id, that does not exists in the datastore, this should cause an error
        # when attempting to save it
        dso = self.get_dummy_saved_dso_1()
        try:
            dso = dSOSqlite3DAO.save([dso])[0]
            self.fail("DSO should not be saved, as the DSO id is not valid")
        except UpdateDAOException:
            assert True
    
    def testPersistCase3(self):
        """ Tests saving a new version of the DSO without the correct last version """
        try:
            dSOSqlite3DAO = DSOSqlite3DAO()
            unsaved_dso = self.get_dummy_unsaved_dso_1()
            dso1 = dSOSqlite3DAO.save([unsaved_dso])[0]
            dSOSqlite3DAO.save([dso1])[0]
            # change the version number to make it look like we are updating with the wrong latest version
            dSOSqlite3DAO.save([dso1])
            self.fail("DSO should not be saved, as the version being updated is not the latest version in the datastore")
        except ConcurrencyDAOException:
            assert True
            
    def testPersistCase4(self):
        """ Tests saving a new version of the DSO by adding a dst"""
        older = self.get_dummy_unsaved_dso_1()
        dSOSqlite3DAO = DSOSqlite3DAO()
        older_dso = dSOSqlite3DAO.save([older])[0]
        dso_id = older_dso.id
        older_dso.add_dsts([self.get_dummy_unsaved_dst_1()])
        newer_dso = dSOSqlite3DAO.save([older_dso])[0]
        older_dso = dSOSqlite3DAO.find_by_id_and_version(dso_id, DEFAULT_VERSION)
        newer_dso = dSOSqlite3DAO.find_by_id_and_version(dso_id, newer_dso.version)
        if older_dso.id == newer_dso.id \
           and older_dso.version != newer_dso.version \
           and len(older_dso.dsts) < len(newer_dso.dsts):
            assert True
        else:
            self.fail("New version was not saved successfully")
    
    def testPersistCase5(self):
        """ Tests saving two new dsos """
        dso1 = self.get_dummy_unsaved_dso_1()
        dso2 = self.get_dummy_unsaved_dso_2()
        dSOSqlite3DAO = DSOSqlite3DAO()
        dsos = dSOSqlite3DAO.save([dso1, dso2])
        if dsos and len(dsos) == 2:
            assert True
        else:
            self.fail("Failed to save dsos")
            
    def testPersistCase6(self):
        """ Tests updating of dsos by removing a dst"""
        dso1 = self.get_dummy_unsaved_dso_1()
        dSOSqlite3DAO = DSOSqlite3DAO()
        saved_dso1 = dSOSqlite3DAO.save([dso1])[0]
        if len(saved_dso1.dsts) == 2:
            dst = saved_dso1.dsts[0]
            saved_dso1.remove_dsts([dst])
            saved_dso2 = dSOSqlite3DAO.save([saved_dso1])[0]
            if saved_dso2 and len(saved_dso2.dsts) == 1:
                assert True
            else:
                self.fail("Failed to delete DSOs")
        else:
            self.fail("Saved Failed")
            
            
    def testPersistCase7(self):
        """ Tests rollback of transaction when updating of one dsos failed """
        dSOSqlite3DAO = DSOSqlite3DAO()
        dso1 = self.get_dummy_unsaved_dso_1()
        dso2 = self.get_dummy_saved_dso_1()
        try:
            saved_dso1 = dSOSqlite3DAO.save([dso1])[0]
            dSOSqlite3DAO.save([saved_dso1, dso2])            
        except UpdateDAOException:
            dso = dSOSqlite3DAO.find_by_ids([1])[0]
            if dso.version == 2:
                self.fail("Failed as one of the dsos got updated even when the trasaction failed")
            else:
                assert True
                
    def testFindByIdAndVersionCase1(self):
        """ Tests finding a dso by id and version """
        dSOSqlite3DAO = DSOSqlite3DAO()
        dso = self.get_dummy_unsaved_dso_1()
        dso = dSOSqlite3DAO.save([dso])[0]
        
        retrieved_dso = dSOSqlite3DAO.find_by_id_and_version(dso.id, dso.version)
        if retrieved_dso and retrieved_dso.id == dso.id:
            assert True
        else:
            self.fail("DSO returned does not have the correct id")
            
    def testFindByIdAndVersionCase2(self):
        """ Tests finding a dso by id and version which doesn't exist """
        dSOSqlite3DAO = DSOSqlite3DAO()
        try:
            dSOSqlite3DAO.find_by_id_and_version(100, "dummy_version")
            self.fail("A exception should have been raised as the dso does not exist")
        except NotFoundDAOException:
            assert True            
        
    def testFindByIdsCase1(self):
        """ Tests finding the latest version of a dso by id"""
        older = self.get_dummy_unsaved_dso_1()
        dSOSqlite3DAO = DSOSqlite3DAO()
        older_dso = dSOSqlite3DAO.save([older])[0]
        newer_dso = dSOSqlite3DAO.save([older_dso])[0]
        dso = dSOSqlite3DAO.find_by_ids([older_dso.id])[0]
        if newer_dso.id == dso.id and newer_dso.version == dso.version:
            assert True
        else:
            self.fail("Latest version returned is incorrect")
            
    def testFindByIdsCase2(self):
        """ Tests finding the latest version of a dso with id that doesn't exist"""
        try:
            dSOSqlite3DAO = DSOSqlite3DAO()
            dSOSqlite3DAO.find_by_ids([100])
            self.fail("Should not return successfully")            
        except NotFoundDAOException:
            assert True

    def testFindByIdsCase3(self):
        """ Tests finding a list of dsos by ids"""
        dso1 = self.get_dummy_unsaved_dso_1()
        dso2 = self.get_dummy_unsaved_dso_2()
        dSOSqlite3DAO = DSOSqlite3DAO()
        dSOSqlite3DAO.save([dso1])
        dSOSqlite3DAO.save([dso2])
        dsos = dSOSqlite3DAO.find_by_ids([1,2])
        if dsos and len(dsos) == 2:
            assert True
        else:
            self.fail("Failed to find two dsos")
    
    def testFindByIdsCase4(self):
        """ Tests finding a list of dsos when one id does not exist """
        try:
            dso1 = self.get_dummy_unsaved_dso_1()
            dSOSqlite3DAO = DSOSqlite3DAO()
            dSOSqlite3DAO.save([dso1])
            dsos = dSOSqlite3DAO.find_by_ids([1,2])
            self.fail("Should thrown an exception since one of those do not exist")
        except NotFoundDAOException:
            assert True        
            
    def testFindByURIAndVersionCase1(self):
        """ Tests finding a dso by uri and version """
        dso = self.get_dummy_unsaved_dso_1()
        dSOSqlite3DAO = DSOSqlite3DAO()
        dSOSqlite3DAO.save([dso])
        returned_dso = dSOSqlite3DAO.find_by_URI_and_version(dso.uri, DEFAULT_VERSION)
        if returned_dso and returned_dso.id is not None:
            assert True
        else:
            self.fail("Failed to find dso");
    
    def testFindByURIAndVersionCase2(self):
        """ Tests finding a dso by uri and version that doesnt exist """
        try:
            dSOSqlite3DAO = DSOSqlite3DAO()
            dSOSqlite3DAO.find_by_URI_and_version("/uri/that/doesnt/exist", DEFAULT_VERSION)
        except NotFoundDAOException:
            assert True
            
    def testFindByURIsCase1(self):
        """ Tests finding the latest version of a dso by URI"""
        older = self.get_dummy_unsaved_dso_1()
        dSOSqlite3DAO = DSOSqlite3DAO()
        older_dso = dSOSqlite3DAO.save([older])[0]
        newer_dso = dSOSqlite3DAO.save([older_dso])[0]
        dso = dSOSqlite3DAO.find_by_URIs([newer_dso.uri])[0]
        if newer_dso.id == dso.id \
           and newer_dso.version == dso.version \
           and newer_dso.uri == dso.uri:
            assert True
        else:
            self.fail("Latest version returned is incorrect")

    def testFindByURIsCase2(self):
        """ Tests finding the latest version of a dso by URI that doesnt exist """
        try:
            dSOSqlite3DAO = DSOSqlite3DAO()
            dSOSqlite3DAO.find_by_URIs(["/uri/that/doesnt/exist"])[0]
            self.fail("Exception should have been raised")            
        except NotFoundDAOException:
            assert True

    def testFindByURIsCase3(self):
        """ Tests finding a list of dsos by uris"""
        dso1 = self.get_dummy_unsaved_dso_1()
        dso2 = self.get_dummy_unsaved_dso_2()
        dSOSqlite3DAO = DSOSqlite3DAO()
        dSOSqlite3DAO.save([dso1])
        dSOSqlite3DAO.save([dso2])
        dsos = dSOSqlite3DAO.find_by_URIs([dso1.uri,dso2.uri])
        if dsos and len(dsos) == 2:
            assert True
        else:
            self.fail("Failed to find two dsos")
    
    def testFindByURIsCase4(self):
        """ Tests finding a list of dsos when one uri does not exist """
        try:
            dso1 = self.get_dummy_unsaved_dso_1()
            dSOSqlite3DAO = DSOSqlite3DAO()
            dSOSqlite3DAO.save([dso1])
            dSOSqlite3DAO.find_by_URIs([dso1.uri,"/uri/that/doesnt/exist"])
            self.fail("Should thrown an exception since one of those do not exist")
        except NotFoundDAOException:
            assert True    
                         
    def testFindVersionsCase1(self):
        """ Tests finding version info by dso id """
        unsaved_dso_ver1 = self.get_dummy_unsaved_dso_1()
        dSOSqlite3DAO = DSOSqlite3DAO()
        dso_ver1 = dSOSqlite3DAO.save([unsaved_dso_ver1])[0]
        dSOSqlite3DAO.save([dso_ver1])
        versions = dSOSqlite3DAO.find_versions(1)
        if versions and len(versions) == 2:
            assert True
        else:
            self.fail("Failed to return version info")
    
    def testFindVersionsCase2(self):
        """ Tests finding version info by dso id which doesnt exist """
        try:
            dSOSqlite3DAO = DSOSqlite3DAO()
            dSOSqlite3DAO.find_versions(1)
            self.fail("Should raise excepetion as id does not exist")
        except NotFoundDAOException:
            assert True
        
    def testDeleteCase1(self):
        """ Tests deleting the dso """
        try:
            unsaved_dso_ver1 = self.get_dummy_unsaved_dso_1()
            dSOSqlite3DAO = DSOSqlite3DAO()
            dso_ver1 = dSOSqlite3DAO.save([unsaved_dso_ver1])[0]
            dSOSqlite3DAO.delete([dso_ver1])
            dSOSqlite3DAO.find_by_ids([dso_ver1.get_id()])
            self.fail("Should return nothing as the dso has been deleted")
        except NotFoundDAOException:
            assert True
            
    def testDeleteCase2(self):
        """ Tests deleting the dso without the latest version """
        try:
            unsaved_dso_ver1 = self.get_dummy_unsaved_dso_1()
            dSOSqlite3DAO = DSOSqlite3DAO()
            dso_ver1 = dSOSqlite3DAO.save([unsaved_dso_ver1])[0]
            dSOSqlite3DAO.save([dso_ver1])[0]
            dSOSqlite3DAO.delete([dso_ver1])
            self.fail("Shouldn't allow delete as you have to use the latest version dso")
        except ConcurrencyDAOException:
            assert True
            
    def testDeleteCase3(self):
        """ Tests deleting the dso and being able to retrieve the specific versions still """
        try:
            unsaved_dso_ver1 = self.get_dummy_unsaved_dso_1()
            dSOSqlite3DAO = DSOSqlite3DAO()
            dso_ver1 = dSOSqlite3DAO.save([unsaved_dso_ver1])[0]
            dSOSqlite3DAO.delete([dso_ver1])
            dso = dSOSqlite3DAO.find_by_id_and_version(dso_ver1.get_id(), dso_ver1.get_version())
            if dso and not dso.active:
                assert True
            else:
                self.fail("Should return the correct version")
        except ConcurrencyDAOException:
            assert True                        
        
def memory_get_connection(is_memory=False):
    return sqlite3.connect(sqlite3dao.DATABASE_NAME)

def get_test_suite():
    sqlite3daoTestSuite = unittest.TestSuite()
    sqlite3daoTestSuite.addTest(SQLite3DAOTest("testDeleteCase1"))
#    sqlite3daoTestSuite.addTest(SQLite3DAOTest("testFindByIdAndVersion"))
    return sqlite3daoTestSuite

if __name__ == "__main__":
#    import sys;sys.argv = ['', 'Test.testName']
    unittest.main()    